//{=====================================================================================================
//! @file    DISASM.cpp
//! @date    2014-11-03 01:17
//! @author  Vladimir Gurinovich <whistleVG@gmail.com>
//!
//!
//! @par      The program realise simple example of disassembler
//!
//! @warning   In disassembler code there are serial number.
//}=====================================================================================================

//! Including libraries

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>


//!-----------------------------------------------------------------------------------------
//! Functions
//!-----------------------------------------------------------------------------------------
int label_analysis(int* labels , int arg_code);

char* analysis_command(int command);
char* Work_with_Register(FILE* code_f , int arg_code);

void Fill_in(FILE* code_f , FILE* dasm , int* labels);

int find_arg(int* command);

void Fill_labels(FILE* labels_f , int* labels);             //!  function , which read labels from file
void Print_label(FILE* dasm , int step , int* labels);      //! function, which analyse position in code and print or no appropriate label

//!-----------------------------------------------------------------------------------------
//! Enum with CPU commands
//!-----------------------------------------------------------------------------------------
enum  CPU_Commands_t
{
    #define DEF_CMD(name,number,code,reg) name = number,
    #include "commands.h"
    #undef DEF_CMD(name,number,code,reg)
    FINISH
};

//!-----------------------------------------------------------------------------------------
//! Type of function
//!-----------------------------------------------------------------------------------------
enum type
{
    NUMBER = 1,
    REGISTER = 2,
    TAG = 3,
    NO_ARG = 0
};


//!-----------------------------------------------------------------------------------------
const int MAX_LABEL_NUMBER = 20;
const int NO_SUCH_LABEL     = 0;          //!< constant, which shows us end of number serial
const int STOP             = -1;

const int AX_CODE = 0;
const int BX_CODE = 1;
const int CX_CODE = 2;
const int DX_CODE = 3;
//!-----------------------------------------------------------------------------------------

int main()
{

    int* labels = (int*)calloc(MAX_LABEL_NUMBER , sizeof(int));

    FILE* dasm = fopen("disasm.txt" , "w");             assert(dasm);
    FILE* code_f = fopen("code.txt" , "r");             assert(code_f);
    FILE* labels_f = fopen("labels.txt" , "r");         assert(labels);

    Fill_labels(labels_f , labels);
    Fill_in(code_f , dasm , labels);

    free(labels); labels = NULL;

    fclose(dasm);
    fclose(labels_f);
    fclose(code_f);

    return 0;
}

//!-----------------------------------------------------------------------------------------

void Fill_labels(FILE* labels_f , int* labels)
{
    assert(labels_f);
    assert(labels);

    int step = 0;
    int command = 0;

    while(true)
    {
        fscanf(labels_f , "%d" , &command);

        if(command == -1) {break;}

        labels[step] = command;
        step++;
    }
}


char* analysis_command(int command)
{
    #define DEF_CMD(name , number ,  code , reg) if(number == command) return #name;
    #include "commands.h"
    #undef DEF_CMD(name , number , code , reg)
}

//!-----------------------------------------------------------------------------------------

void Fill_in(FILE* code_f , FILE* dasm , int* labels)
{
    assert(dasm);
    assert(code_f);
    assert(labels);

    int command = 0;
    int arg_code = 0;

    char* reg_id = NULL;
    char* command_name = NULL;
    char* reg = NULL;

    int number = 0;
    int step = 0;

    while(command != EXIT)
    {

    fscanf(code_f , "%d" , &command);
    command_name = analysis_command(command);


        switch(find_arg(&command))
        {

                case NUMBER:

                    fscanf(code_f , "%d" , &arg_code);
                    fprintf(dasm , "%s " , command_name);
                    step++;

                    Print_label(dasm , step , labels);

                    fprintf(dasm , "%d\n", arg_code);
                    step++;

                    Print_label(dasm , step , labels);

                    break;

                case REGISTER:

                    fscanf(code_f , "%d" , &arg_code);
                    reg = Work_with_Register(code_f , arg_code);

                    fprintf(dasm , "%s " , command_name);
                    step++;

                    Print_label(dasm , step , labels);

                    fprintf(dasm , "%s\n", reg);
                    step++;

                    Print_label(dasm , step , labels);


                    break;

                case TAG:

                     fprintf(dasm , "%s " , command_name);
                     step++;

                     Print_label(dasm , step ,labels);

                     fscanf(code_f , "%d" , &arg_code);

                     number = label_analysis(labels , arg_code);

                     fprintf(dasm , "%d\n", number);
                     step++;

                     Print_label(dasm , step , labels);

                     break;

                case NO_ARG:

                     fprintf(dasm , "%s\n" , command_name);
                     step++;

                     Print_label(dasm , step , labels);

                     break;

                default : printf("ERROR");

        }

    }

}

//!-----------------------------------------------------------------------------------------
//! Functions, which work with labels

void Print_label(FILE* dasm , int step , int* labels)
{

    assert(labels);
    assert(dasm);
    if(label_analysis(labels , step ) != NO_SUCH_LABEL)
    {
        fprintf(dasm , ":%d\n" , label_analysis(labels , step ) );
    }

}

int label_analysis(int* labels , int arg_code)
{

    assert(labels);
    for(int number = 0; number < MAX_LABEL_NUMBER; number++)
    {

        if(labels[number] == arg_code)
        {
            return number + 1;
        }

    }
    return NO_SUCH_LABEL;
}

//!-----------------------------------------------------------------------------------------

char* Work_with_Register(FILE* code_f , int arg_code )
{

    assert(code_f);
    switch(arg_code)
    {
        case AX_CODE: return "ax";
        case BX_CODE: return "bx";
        case CX_CODE: return "cx";
        case DX_CODE: return "dx";
    }

}

int find_arg(int* command)
{

    assert(command);
    switch(*command)
    {

        #define DEF_CMD(name , number , code , reg) case number : return reg; break;
        #include "commands.h"
        #undef DEF_CMD(name , number , code , reg)

    }
}

//!-----------------------------------------------------------------------------------------












